package dao

import (
	"OJCenter/dao/mysql"
	"OJCenter/dao/redis"
	"OJCenter/global"
	"OJCenter/idl"
	"errors"
	"strconv"
	"time"
	"xorm.io/xorm"
)

const userInfoPre = "u"
const REDIS_UID = "uid"
const REDIS_STATUS = "status"
const REDIS_PRIVILEGE = "privilege"

const userMailInfoPre = "um"

type UserInfo struct {
	mysql *mysql.MysqlDao
	redis *redis.RedisDao
}

func NewUserInfo() (*UserInfo, error) {
	m, err := mysql.NewMysqlDao()
	if err != nil {
		return nil, err
	}
	r, err := redis.NewRedisDao()
	if err != nil {
		return nil, err
	}
	return &UserInfo{
		mysql: m,
		redis: r,
	}, nil
}

func (u *UserInfo) GetUserInfoFromDataByID(uid int64) (*idl.User, error) {
	var user idl.User
	has, err := u.mysql.Orm.Where("uid=?", uid).Get(&user)

	if err != nil && err != xorm.ErrNotExist {
		return nil, err
	}
	if !has {
		err = xorm.ErrNotExist
		return nil, err
	}
	return &user, nil
}

func (u *UserInfo) GetUserList(page int, limit int) (*[]idl.User, error) {
	var users []idl.User
	err := u.mysql.Orm.Where("is_disable = ?", false).Limit(limit, (page-1)*limit).Find(&users)
	if err != nil {
		return nil, err
	}
	return &users, nil
}

func (u *UserInfo) SelectUsers(info *idl.User) (*[]idl.User, error) {

	var rets []idl.User
	err := u.mysql.Orm.Table("user").Find(&rets, info)
	if err != nil {
		return nil, err
	}
	return &rets, nil
}

func (u *UserInfo) GetUserInfoFromRedis(uid int64) (*map[string]string, error) {
	var UInfoMap map[string]string
	UInfoMap, err := u.redis.GetMap(userInfoPre + strconv.Itoa(int(uid)))
	if err != nil {
		if err != errors.New("redis cache is nil") {
			global.Logger.Infof("GetUserInfoFromRedis failed,err:%v", err)
		}
	}
	return &UInfoMap, nil
}

func (u *UserInfo) GetUserInfoWithRedis(uid int64) (*map[string]string, error) {
	UInfoMap, err := u.GetUserInfoFromRedis(uid)
	if err != nil {
		info, err := u.GetUserInfoFromDataByID(uid)
		args, err := UInfoToMap(info)
		if err != nil {
			return nil, err
		}
		err = u.redis.PutMapWithDeadline(userInfoPre+strconv.Itoa(int(uid)), *args)
		if err != nil {
			return nil, err
		}
		return args, nil
	}

	return UInfoMap, nil
}

func (u *UserInfo) IsCacheHasUInfo(uid int64) (bool, error) {
	return u.redis.IsKeyAli(userInfoPre + strconv.Itoa(int(uid)))
}

func UInfoToMap(info *idl.User) (*map[string]string, error) {
	if info == nil {
		return nil, errors.New("info is nil")
	}
	args := map[string]string{
		REDIS_UID:       strconv.Itoa(int(info.Uid)),
		REDIS_STATUS:    strconv.Itoa(info.Status),
		REDIS_PRIVILEGE: info.AdminType,
	}
	return &args, nil
}

func (u *UserInfo) DeleteUserInfo(info *idl.User) error {
	if info == nil {
		return errors.New("info is nil")
	}
	has, err := u.IsCacheHasUInfo(info.Uid)
	if err != nil {
		global.Logger.Errorf("select key failed,err:%v", err)
	}
	if has {
		_, err := u.redis.DelKey(userInfoPre + strconv.Itoa(int(info.Uid)))
		if err != nil {
			global.Logger.Errorf("del key failed,err:%v", err)
		}
	}
	cond := make(map[string]interface{})
	cond["uid"] = info.Uid
	sum, err := u.mysql.DeleteInfos("user", &cond)
	if err != nil || sum == 0 {
		return errors.New("delete user failed")
	}
	return nil

}

func (u *UserInfo) UpdateUserInfoByID(info map[string]interface{}) error {
	if info == nil {
		return errors.New("info is nil")
	}
	has, err := u.IsCacheHasUInfo(info["uid"].(int64))
	if err != nil {
		global.Logger.Errorf("select key  failed,err:%v", err)
	}
	if has {
		_, err := u.redis.DelKey(userInfoPre + strconv.Itoa(int(info["uid"].(int64))))
		if err != nil {
			global.Logger.Errorf("del key failed,err:%v", err)
		}
	}

	cond := make(map[string]interface{})
	cond["uid"] = info["uid"].(int64)
	sum, err := u.mysql.UpdateInfos("user", &info, &cond)
	if err != nil || sum == 0 {
		return errors.New("update user failed")
	}
	return nil
}

func (u *UserInfo) InsertUserInfo(info *idl.User) error {
	user, err := u.GetUserInfoFromDataByID(info.Uid)
	if err != xorm.ErrNotExist {
		return err
	}
	sum, err := u.mysql.InsertInfos("user", user)
	if err != nil || sum == 0 {
		return err
	}
	return nil
}

func (u *UserInfo) InsertMailCodeInfo(uid int64, Code uint32) error {
	err := u.redis.SetString(userMailInfoPre+strconv.Itoa(int(uid)), strconv.Itoa(int(Code)), time.Minute*5)
	if err != nil {
		return err
	}
	return nil
}
func (u *UserInfo) GetMailInfo(uid int64) (uint32, error) {
	code, err := u.redis.GetString(userMailInfoPre + strconv.Itoa(int(uid)))
	if err != nil {
		return 0, err
	}
	_code, err := strconv.Atoi(code)
	c := uint32(_code)
	return c, nil
}

func (u *UserInfo) DelMailInfo(uid int64, Code uint32) error {
	err := u.redis.SetString(userMailInfoPre+strconv.Itoa(int(uid)), strconv.Itoa(int(Code)), time.Minute*5)
	if err != nil {
		return err
	}
	return nil
}
